Skip to content

fix: use libphonenumber getRegionCodeForNumber for NANP area codes (787->PR, 809->DO, ...) - #11

Open
gaoflow wants to merge 1 commit into
gilshallem:masterfrom
gaoflow:fix/nanp-missing-areacode-getregioncodefornumber
Open

fix: use libphonenumber getRegionCodeForNumber for NANP area codes (787->PR, 809->DO, ...)#11
gaoflow wants to merge 1 commit into
gilshallem:masterfrom
gaoflow:fix/nanp-missing-areacode-getregioncodefornumber

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 25, 2026

Copy link
Copy Markdown

Summary

parsePhone() returns a self-contradictory result for many NANP (North American Numbering Plan) numbers. For example:

parsePhone("17871234567")
// { countryCode: "1", areaCode: "787", number: "1234567", countryISOCode: "nothing" }

The areaCode: "787" is correct — Puerto Rico — and it was parsed by the bundled libphonenumber. Yet countryISOCode: "nothing". The library contradicts its own bundled libphonenumber, whose getRegionCodeForNumber() returns "PR" for the same input.

The library is even inconsistent with itself: the overlay 939 resolves to "PR" (it is in the prefixes table) while the primary 787 resolves to "nothing" (it is not). Same country, opposite answers.

Root cause

countryISOCode is derived from a hand-rolled, incomplete prefixes table via findCountryISO2, which walks prefixes of the input and looks them up in the map. NANP numbers under country code 1 are listed per-area-code (e.g. 1849: "DO", 1939: "PR"), but many area codes are missing:

Input Actual Expected (NANPA + bundled libphonenumber)
17871234567 nothing PR (787 Puerto Rico)
18091234567 nothing DO (809 Dominican Republic)
18291234567 nothing DO (829 Dominican Republic overlay)
12681234567 nothing AG (268 Antigua & Barbuda)
1284…, 1340…, 1649…, 1784…, 1868…, 1869… nothing VG, VI, TC, VC, TT, KN

All of these missing area codes are already known to the bundled libphonenumber, which the package uses to parse areaCode into the very same result object.

Fix

Prefer the bundled libphonenumber's authoritative region. After parsing the number with the existing default region, override countryISOCode with PhoneNumberUtil.getRegionCodeForNumber(r) when it returns a non-null region, falling back to the prefixes table only when libphonenumber cannot determine a region (e.g. invalid numbers). This keeps countryISOCode consistent with the areaCode libphonenumber already parses.

         var t = i18n.phonenumbers.PhoneNumberUtil.getInstance()
           , n = findCountryISO2(e)
           , o = t.getRegionCodeForCountryCode(n)
           , r = t.parseAndKeepRawInput("+" + e, o)
           , d = o = null
           , i = t.getNationalSignificantNumber(r)
           , N = t.getLengthOfNationalDestinationCode(r);
+        var regionForNumber = t.getRegionCodeForNumber(r);
+        null != regionForNumber && (n = regionForNumber);

The prefixes table and findCountryISO2 are retained as the fallback path and to compute the initial default region for parsing, so non-NANP numbers (e.g. +268 Eswatini → SZ) and libphonenumber-null cases are unaffected.

Tests

Added test.js (node assert; the package previously had no test suite — npm test was a stub). Covers:

  • RED cases (now fixed): 1787→PR, 1809→DO, 1829→DO, 1268→AG, plus 1284→VG, 1340→VI, 1649→TC, 1784→VC, 1868→TT, 1869→KN.
  • Controls (preserved): 1939→PR, 1849→DO, 1202→US.
  • Internal consistency: parsePhone("17871234567") has areaCode === "787" AND countryISOCode === "PR" (the original self-contradiction is gone).
  • Non-NANP regression guard: parsePhone("26822171234").countryISOCode === "SZ" (Eswatini, country calling code +268, unchanged).
$ node test.js
All tests passed.

Scope

Only index.js (2 lines) and a new test.js. findCountryISO2, the prefixes table, and the bundled libphonenumber metadata are unchanged. No new dependencies.

…ution

parsePhone() derived countryISOCode from a hand-rolled, incomplete prefixes
table via findCountryISO2, ignoring the bundled libphonenumber's
getRegionCodeForNumber(). NANP area codes missing from the table (787, 809,
829, 268, 284, 340, 649, 784, 868, 869, ...) returned countryISOCode
"nothing" even though the same result object carried the correct areaCode
parsed by libphonenumber.

This produced a self-contradictory result, e.g. parsePhone("17871234567")
returned { areaCode: "787", countryISOCode: "nothing" } -- the library
contradicted its own bundled libphonenumber, which resolves 787 to PR.
Overlay 939 resolved to "PR" while the primary 787 resolved to "nothing".

Fix: after parsing the number with the existing default region, prefer the
authoritative region returned by PhoneNumberUtil.getRegionCodeForNumber()
for countryISOCode, falling back to the prefixes table only when
libphonenumber cannot determine a region (e.g. invalid numbers). This keeps
countryISOCode consistent with the areaCode libphonenumber already parses.

Resolves all missing NANP area codes (787->PR, 809/829->DO, 268->AG, 284->VG,
340->VI, 649->TC, 784->VC, 868->TT, 869->KN) and preserves existing behavior
(939->PR, 849->DO, 1202->US, and non-NANP such as +268 Eswatini).

Added test.js (node assert) covering the RED cases, controls, and the
internal-consistency check (areaCode vs countryISOCode).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant